﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System;

public class ABManager : MonoBehaviour
	{
		#region 单例
		private static ABManager instance;
		public static ABManager Instance
		{
			get
			{
				if (instance == null)
				{
					var obj = new GameObject();
					instance = obj.AddComponent<ABManager>();
					obj.name = instance.ToString();
					DontDestroyOnLoad(obj);
				}
				return instance;
			}
		}

		public static bool IsInitialized => instance != null;
		/// <summary> 覆盖默认访问器 </summary>
		[HideInInspector] public new Transform transform;

		private void Awake()
		{
			if (instance != null)
			{
				Destroy(gameObject);
			}
			else
			{
				instance = (ABManager)this;
				transform = GetComponent<Transform>();
			}

		}

		private void OnDestroy()
		{
			instance = null;
		}

        public override string ToString()
        {
            return "AB包管理器";
        }

		#endregion

		#region 字段

		// AB包管理器目的是：让外部更方便地进行资源加载
		// AB包不能重复加载，重复加载会报错，其中的资源不限制
		// 用字典来存储加载过的AB包
		private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
		// 主包和相关配置文件只需要加载一次即可，因此声明相关变量
		// 主包
		private AssetBundle mainAB = null;
		// 依赖包获取用的配置文件
		private AssetBundleManifest manifest = null;

		private string AssetBundleManifest = "AssetBundleManifest";
		#endregion

		#region 同步加载

		/// <summary> 
		///  确保了每个包都只加载了一次, 并加载依赖包
		/// </summary>
		public void LoadAB(string abName)
		{
			// 加载AB主包和其中的关键配置文件
			if (mainAB == null)
			{
				mainAB = AssetBundle.LoadFromFile(ABPackVersionUpdate.ABPathUrl + ABPackVersionUpdate.ABMainName);
				manifest = mainAB.LoadAsset<AssetBundleManifest>(AssetBundleManifest);
			}

			// 获取依赖包的信息
			AssetBundle ab = null;
			string[] strs = manifest.GetAllDependencies(abName);

			for (int i = 0; i < strs.Length; ++i)
			{
				if (!abDic.ContainsKey(strs[i]))
				{
					ab = AssetBundle.LoadFromFile(ABPackVersionUpdate.ABPathUrl + strs[i]);
					abDic.Add(strs[i], ab);
				}
			}

			// 加载资源来源包
			// 如果没有加载过，再加载
			if (!abDic.ContainsKey(abName))
			{
				ab = AssetBundle.LoadFromFile(ABPackVersionUpdate.ABPathUrl + abName);
				abDic.Add(abName, ab);
			}
		}

		/// <summary>
		///  获取ab包
		/// </summary>
		public AssetBundle GetAB(string abName)
		{
			if (abDic.TryGetValue(abName, out var ab))
			{
				return ab;
			}
			return null;
		}

		/*
		///  对同步加载进行重载，因为通过泛型可以避免as转换，并且Lua不支持泛型，
		///  因此还需要使用type重载
		///  同步加载, 不指定类型
		 */

		/// <summary> 
		///  同步加载，加载abName包中的resName资源
		/// </summary>
		public UnityEngine.Object LoadRes(string abName, string resName)
		{
			// 加载AB包
			LoadAB(abName);
			// 加载资源
			var obj = abDic[abName].LoadAsset(resName);
			return obj;
		}

		/// <summary>
		///  同步加载，根据type指定类型
		/// </summary>
		public UnityEngine.Object LoadRes(string abName, string resName, System.Type type)
		{
			// 加载AB包
			LoadAB(abName);
			// 加载资源
			var obj = abDic[abName].LoadAsset(resName, type);
			return obj;
		}

		/// <summary> 
		///  同步加载，根据泛型指定类型，必须要加约束，因为LoadAsset<T>方法带有约束
		/// </summary>
		public T LoadRes<T>(string abName, string resName) where T : UnityEngine.Object
		{
			// 加载AB包
			LoadAB(abName);
			// 加载资源
			T obj = abDic[abName].LoadAsset<T>(resName);
			return obj;
		}

		#endregion

		#region 异步加载
		/*
		/// 异步加载的方法，由于异步加载无法马上使用资源，需要使用委托
		/// 来知道资源加载完后应该怎样使用资源
		/// 这里的异步加载，AB包并没有使用异步加载
		/// 只是从AB包中加载资源时，使用异步
		/// 和同步一样重载
		/// 根据名字异步加载资源
		 */

		/// <summary> 异步加载，确保了每个包都只加载了一次, 并加载依赖包 </summary>
		public void LoadABAsync(string abName, Action<AssetBundle> callBack)
        {
			StartCoroutine(ReallyLoadABAsync(abName, callBack));
        }

		/// <summary>
		///  异步加载资源，加载abName包中的resName资源
		/// </summary>
		public void LoadResAsync(string abName, string resName, Action<UnityEngine.Object> callBack)
		{
			StartCoroutine(ReallyLoadABAsync(abName, (ab) =>
			{
				StartCoroutine(ReallyLoadResAsync(ab, resName, callBack));
			}));
		}

		/// <summary>
		///  根据type异步加载资源
		/// </summary>
		public void LoadResAsync(string abName, string resName, System.Type type, Action<UnityEngine.Object> callBack)
		{
			StartCoroutine(ReallyLoadABAsync(abName, (ab) =>
			{
				StartCoroutine(ReallyLoadResAsync(ab, resName, type, callBack));
			}));
		}

		/// <summary>
		///  根据泛型异步加载资源
		/// </summary>
		public void LoadResAsync<T>(string abName, string resName, Action<UnityEngine.Object> callBack) where T : UnityEngine.Object
		{
			StartCoroutine(ReallyLoadABAsync(abName, (ab) =>
			{
				StartCoroutine(ReallyLoadResAsync<T>(ab, resName, callBack));
			}));
		}

		#endregion

		#region 卸载包

		/// <summary> 单个包的卸载 </summary>
		public static void UnLoadAB(string abName, bool unloadAllLoadedObjects = false)
		{
			if (Instance.abDic.ContainsKey(abName))
			{
				Instance.abDic[abName].Unload(unloadAllLoadedObjects);
				Instance.abDic.Remove(abName);
			}
		}

		/// <summary> 卸载指定包和其依赖包 </summary>
		public static void UnLoadAB_rely(string abName, bool unloadAllLoadedObjects = false)
        {
			// 获取依赖包名
			string[] strs = instance.manifest.GetAllDependencies(abName);
			// 卸载依赖包
			for (int i = 0; i < strs.Length; ++i)
			{
				var key = strs[i];
				if (instance.abDic.ContainsKey(key))
				{
					Instance.abDic[key].Unload(unloadAllLoadedObjects);
					instance.abDic.Remove(key);
				}
			}
			// 卸载包
			if (Instance.abDic.ContainsKey(abName))
			{
				Instance.abDic[abName].Unload(unloadAllLoadedObjects);
				Instance.abDic.Remove(abName);
			}
		}

		/// <summary> 所有包的卸载 </summary>
		public static void ClearAB(bool unloadAllLoadedObjects = false)
		{
			AssetBundle.UnloadAllAssetBundles(unloadAllLoadedObjects);
			Instance.abDic.Clear();
			Instance.mainAB = null;
			Instance.manifest = null;
		}
		
		#endregion

		#region 异步加载协程

		/// <summary> 异步加载，确保了每个包都只加载了一次, 并加载依赖包 </summary>
		private IEnumerator ReallyLoadABAsync(string abName, Action<AssetBundle> callBack = null)
        {
			// 加载AB主包和其中的关键配置文件
			if (mainAB == null)
			{
				var request = AssetBundle.LoadFromFileAsync(ABPackVersionUpdate.ABPathUrl + ABPackVersionUpdate.ABMainName);

				yield return new WaitUntil(() => request.isDone);
				mainAB = request.assetBundle;
				manifest = mainAB.LoadAsset<AssetBundleManifest>(AssetBundleManifest);
			}

			// 获取依赖包的信息
			string[] strs = manifest.GetAllDependencies(abName);

			for (int i = 0; i < strs.Length; ++i)
			{
				var key = strs[i];
				if (!abDic.ContainsKey(key))
				{
					var request = AssetBundle.LoadFromFileAsync(ABPackVersionUpdate.ABPathUrl + key);
					yield return new WaitUntil(() => request.isDone);
					abDic.Add(key, request.assetBundle);
				}
			}

			// 加载资源来源包
			// 如果没有加载过，再加载
			if (!abDic.ContainsKey(abName))
			{
				var request = AssetBundle.LoadFromFileAsync(ABPackVersionUpdate.ABPathUrl + abName);
				yield return new WaitUntil(() => request.isDone);
				abDic.Add(abName, request.assetBundle);
			}

			// 异步加载结束后，通过委托传递给外部来使用
			callBack?.Invoke(abDic[abName]);
		}

		private IEnumerator ReallyLoadResAsync(AssetBundle ab, string resName, Action<UnityEngine.Object> callBack)
		{
			AssetBundleRequest abr = ab.LoadAssetAsync(resName);
			yield return abr;

			// 异步加载结束后，通过委托传递给外部来使用
			callBack(abr.asset);
		}

		private IEnumerator ReallyLoadResAsync(AssetBundle ab, string resName, System.Type type, Action<UnityEngine.Object> callBack)
		{
			AssetBundleRequest abr = ab.LoadAssetAsync(resName, type);
			yield return abr;

			// 异步加载结束后，通过委托传递给外部来使用
			callBack(abr.asset);
		}

		private IEnumerator ReallyLoadResAsync<T>(AssetBundle ab, string resName, Action<T> callBack) where T : UnityEngine.Object
		{
			AssetBundleRequest abr = ab.LoadAssetAsync<T>(resName);
			yield return abr;

			// 异步加载结束后，通过委托传递给外部来使用
			callBack(abr.asset as T);
		}

		/// <summary>
		/// 从网络加载AB包
		/// </summary>
		/// <param name="path">url地址</param>
		/// <param name="name">对象名</param>
		/// <returns></returns>
		private IEnumerator LoadFromUnityWebRequest(string path, string name)
		{
			//  从本地加载
			string url = @"file:///" + path;
			//  从服务器加载
			//string url = @"http://localhost/AssetBundles/...";
			UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url);
			//  使用Send方法进行下载(向url地址发送请求)
			yield return request.SendWebRequest();
			//  downloadHandler => 下载器的基类
			AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
			//  已知文件加载
			GameObject obj = ab.LoadAsset<GameObject>(name);
			Transform canvas = GameObject.Find("Canvas").transform;
			//Instantiate(obj, canvas);
		}

		//IEnumerator LoadFromUnityWebRequestLua()
		//{
		//    string url = @"file:///" + pathLua;

		//    UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(url);
		//    yield return request.SendWebRequest();

		//    AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
		//    TextAsset lua = ab.LoadAsset<TextAsset>("test.lua");

		//    LuaEnv luaenv = new LuaEnv();
		//    luaenv.DoString(lua.text);
		//}

		#endregion
	}